﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Security.Principal;
using System.Threading.Tasks;
using Microsoft.AspNet.Identity;
using Microsoft.AspNet.Identity.Owin;
using Microsoft.Owin;
using Microsoft.Owin.Security.Cookies;
using Microsoft.Owin.Security.Google;
using Microsoft.Owin.Security.Infrastructure;
using Microsoft.Owin.Security.OAuth;
using Owin;
using Test.Service.Models;

namespace Test.Service
{
    public partial class Startup
    {
        private readonly ConcurrentDictionary<string, string> authenticationCodes =
          new ConcurrentDictionary<string, string>(StringComparer.Ordinal);

        public void ConfigureAuth(IAppBuilder app)
        {
            // 使应用程序可以使用 Cookie 来存储已登录用户的信息
            // 并使用 Cookie 来临时存储有关使用第三方登录提供程序登录的用户的信息
            // 配置登录 Cookie
            app.UseCookieAuthentication(new CookieAuthenticationOptions
            {
                AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
                LoginPath = new PathString("/Account/Login"),
                LogoutPath = new PathString("/Account/LogOff"),
            });

            app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());

            app.UseOAuthAuthorizationServer(new OAuthAuthorizationServerOptions
            {
                AuthorizeEndpointPath = new PathString("/Authorize"),
                TokenEndpointPath = new PathString("/Token"),
                ApplicationCanDisplayErrors = true,
                AllowInsecureHttp = true,
                // Authorization server provider which controls the lifecycle of Authorization Server
                // Authorization server provider 控制认证服务器的生命周期
                Provider = new OAuthAuthorizationServerProvider
                {
                    OnValidateClientRedirectUri = ValidateClientRedirectUri,
                    OnValidateClientAuthentication = ValidateClientAuthentication,
                    OnGrantResourceOwnerCredentials = GrantResourceOwnerCredentials,
                    OnGrantClientCredentials = GrantClientCredetails,
                    OnTokenEndpoint = TokenEndpoint
                },

                // Authorization code provider which creates and receives authorization code
                // Authorization code provider 创建和接收认证码
                AuthorizationCodeProvider = new AuthenticationTokenProvider
                {
                    OnCreate = CreateAuthenticationCode,
                    OnReceive = ReceiveAuthenticationCode,
                },

                // Refresh token provider which creates and receives referesh token
                // Refresh token provider 创建和接收访问凭据访问凭据
                RefreshTokenProvider = new AuthenticationTokenProvider
                {
                    OnCreate = CreateRefreshToken,
                    OnReceive = ReceiveRefreshToken,
                }
            });
        }

        private void ReceiveRefreshToken(AuthenticationTokenReceiveContext context)
        {
            context.DeserializeTicket(context.Token);
        }

        private void CreateRefreshToken(AuthenticationTokenCreateContext context)
        {
            context.SetToken(context.SerializeTicket());
        }

        private void ReceiveAuthenticationCode(AuthenticationTokenReceiveContext context)
        {
            string value;
            if (authenticationCodes.TryRemove(context.Token, out value))
            {
                context.DeserializeTicket(value);
            }
        }

        private void CreateAuthenticationCode(AuthenticationTokenCreateContext context)
        {
            string tokenValue = Guid.NewGuid().ToString("n") + Guid.NewGuid().ToString("n");
            context.SetToken(tokenValue);
            string authenticationCode = context.SerializeTicket();
            authenticationCodes[context.Token] = authenticationCode;
        }

        /// <summary>
        /// Context中的属性加入到token中
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        public Task TokenEndpoint(OAuthTokenEndpointContext context)
        {
            foreach (KeyValuePair<string, string> property in context.Properties.Dictionary)
            {
                context.AdditionalResponseParameters.Add(property.Key, property.Value);
            }
            return Task.FromResult<object>(null);
        }


        /// <summary>
        /// 客户端权限验证模式（client credentials）
        /// 获取客户端凭证
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        private Task GrantClientCredetails(OAuthGrantClientCredentialsContext context)
        {
            var identity = new ClaimsIdentity(
                new GenericIdentity(context.ClientId, OAuthDefaults.AuthenticationType),
                context.Scope.Select(x => new Claim("urn:oauth:scope", x))
                );
            context.Validated(identity);
            return Task.FromResult(0);
        }


        /// <summary>
        /// 密码模式（resource owner password credentials）
        /// 验证客户端用户名密码，给予给客户端用户授权,客户端需使用POST方式将用户名密码发送到服务器(OAuth/Token)。
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        private Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
        {
            var identity = new ClaimsIdentity(
                new GenericIdentity(context.UserName, OAuthDefaults.AuthenticationType),
                context.Scope.Select(x => new Claim("urn:oauth:scope", x))
                );

            context.Validated(identity);

            return Task.FromResult(0);
        }

        /// <summary>
        /// 校验客户端的请求头和请求体获取用户凭证
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        private Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
        {
            string clientId;
            string clientSecret;
            if (context.TryGetBasicCredentials(out clientId, out clientSecret) ||
                context.TryGetFormCredentials(out clientId, out clientSecret))
            {
                // 验证客户端标识与安全码
                if (clientId == "123" && clientSecret == "123")
                {
                    context.Validated();
                }
                // 验证客户端标识与安全码
                if (clientId == "222")
                {
                    context.Validated();
                }
            }
            return Task.FromResult(0);
        }


        /// <summary>
        /// 校验客户端地址是否已经在验证服务器中进行注册
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        private Task ValidateClientRedirectUri(OAuthValidateClientRedirectUriContext context)
        {
            // 验证客户端标识与重定向地址
            if (context.ClientId == "123")
            {
                //context.RedirectUri = context.RedirectUri.Substring(0, context.RedirectUri.IndexOf('?'));
                context.Validated("http://lhl.client.com/Account/CallBack");
            }
            // 验证客户端标识与重定向地址
            if (context.ClientId == "222")
            {
                context.Validated("http://lhl.client.com/Account/CallBack.html"+ context.RedirectUri.Substring(context.RedirectUri.IndexOf('?')));
            }
            return Task.FromResult(0);
        }

    }
}